
                     // Codigo fuente //

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "heap.h"
#include "hash.h"
#include "lista.h"
#include "quadtree.h"
#define MAX_LINEA 250

struct punto{
	int x;
	int y;
};

typedef struct _sucursal{
	punto_t* coordenadas;
	char *nombre;
}sucursal_t;

typedef struct _pedido{
	size_t cant_pizzas;
	size_t prioridad;
	char* telefono;
	sucursal_t *sucursal;
}pedido_t;


char* duplicar(const char *clave){
	char *nueva = malloc(sizeof(char)*strlen(clave)+1);
	if(!nueva) return NULL;
	strcpy(nueva, clave);
	return nueva;
}

/*Crea una estructura sucursal y la inicializa. En caso de no poder crearla devuelve NULL
 * Post: se devolvió una estructura sucursal inicializada
 * */
sucursal_t *sucursal_crear(char *nombre, int x, int y){
	sucursal_t *sucursal = malloc(sizeof(sucursal_t));
	if(!sucursal) return NULL;
	sucursal->coordenadas = malloc(sizeof(punto_t));
	if(!sucursal->coordenadas){
		free(sucursal);
		return NULL;
	}
	sucursal->coordenadas->x = x;
	sucursal->coordenadas->y = y;
	sucursal->nombre = duplicar(nombre);
	if(!sucursal->nombre){
			free(sucursal->coordenadas);
			free(sucursal);
			return NULL;
	}
	return sucursal;
	
}

/*Función de destrucción de estructura sucursal
 * Pre: estructura creada*/
void sucursal_destruir(sucursal_t *sucursal){
	free(sucursal->nombre);
	free(sucursal->coordenadas);
	free(sucursal);
}

/*Crea y devuelve una estructura punto
 * Post: se devolvio una estructura inicializada, si no pudo crearse
 * devuelve NULL*/
punto_t * punto_crear(int x, int y){
	punto_t * punto = malloc(sizeof(punto_t));
	if(!punto) return NULL;
	punto->x = x;
	punto->y = y;
	return punto;
}

/*Función de destrucción de la estructura punto
 * Pre: estructura creada*/
void punto_destruir(punto_t * punto){
	free(punto);
}

/*Crea una estructura pedido y la inicializa
 * Post: se devolvió una estructura pedido inicializada, si no se pudo
 * crear devuelve NULL*/
pedido_t* pedido_crear(sucursal_t *sucursal, size_t prioridad, size_t cant_pizzas,char* telefono){
		pedido_t* pedido = malloc(sizeof(pedido_t));
		if(!pedido) return NULL;
		pedido->sucursal = sucursal;
		pedido->telefono = duplicar(telefono);
		if(!pedido->telefono){
			free(pedido);
			return NULL;
		}
		pedido->cant_pizzas = cant_pizzas;
		pedido->prioridad = prioridad;
		return pedido;
}

/*Función de destrucción de la estructura pedido
 * Pre: estructura creada*/
void pedido_destruir(void* dato){
	pedido_t* pedido = dato;	

	free(pedido->telefono);
	free(pedido);
}

/*Función de comparación del heap, compara las prioridades del pedido.*/
int comparacion_heap(const void* pedido_1 ,const void* pedido_2){
	
	const pedido_t* x = pedido_1;
	const pedido_t* y = pedido_2;	
	
	if(x->prioridad < y->prioridad) return -1;
	if(x->prioridad == y->prioridad) return 0;

	return 1;


}

/*Separa la linea por campos delimitados por ;\n\r\t y los devuele en los parámetros
 * recibidos por la función
 * Pre: linea , nombre , x, y son posiciones válidas de memoria donde se puede escribir
 * Post: linea nombre x y contienen los campos correspondientes*/
bool separar_por_campos(char *linea, char **nombre, int *x, int *y){
	const char *DELIM = ";\n\r\t";
	char* campos[3];
	int i = 1;
	char *palabra = strtok(linea, DELIM);
	campos[0] = palabra;
	while(palabra != NULL && i<3){
		palabra = strtok(NULL, DELIM);
		if(!palabra) return false;
		campos[i] = palabra; 
		i++;
	}
	*nombre = campos[0];
	*x = atoi(campos[1]);
	*y = atoi(campos[2]);
	return true;
	
}

/*Abre el archivo asociado al nombre pasado por parámetro. Lee linea a linea y la separa por campos. Crea estructuras
 * sucursal por cada línea del archivo y las guarda en el quadtree.
 * Pre: quadtree creado
 * Post: el quadtree contiene estructuras sucursal
 * */
bool cargar_sucursales(char *nombre_archivo,quadtree_t* quadt){
	char linea[MAX_LINEA] = " ";
	char* nombre = " " ;
	int x = 0;
	int y = 0;
	
	FILE *arch_sucursales = fopen(nombre_archivo, "r");

	if( !arch_sucursales ) return false;
	
	fgets(linea, MAX_LINEA, arch_sucursales);	
	
	while(!feof(arch_sucursales)){		
		separar_por_campos(linea, &nombre, &x, &y);
		sucursal_t* sucursal = sucursal_crear(nombre,x,y);
		quadtree_guardar(quadt,sucursal);
		fgets(linea, MAX_LINEA, arch_sucursales);		
	}
	fclose(arch_sucursales);
	return true;

}

/*Dada una línea, cuenta cuantos campos hay delimitados por un espacio ' ' o por comillas segun 
 * el campo que se deba contar.
 * Pre: linea es algo ingresado por el usuario por stdin.
 * Post: se devolivió la canitidad de campos en la línea */ 
int contar_cantidad_campos(char *linea){
		int cant_campos = 0;
		int i= 0;
		int j= 0;
		while(linea[i] != '\n'){
				if(cant_campos == 1){
					if(linea[i] == '"'){
						j++;
						if (j == 2 ) cant_campos++;
					}		
				}
				else if (linea[i] == ' ') cant_campos++;

				i++;
		}
		return cant_campos;



}

/* Cuenta la cantida de campos con la función contar_cantidad_de_campos y crea un vector del tamaño
 * devuelto por la función. Separa los campos por los delimitadores " \n" o " \"" y devuelve en el vector 
 * el contenido de los campos
 * Pre: linea es algo ingresado por el usuario por stdin.
 * Post: se devolvió un vector con la cantidad de campos y contenido
 * correspondiente */
char** parsear_linea(char *linea){
	int cant_campos = contar_cantidad_campos(linea);
	char **campos=malloc(sizeof(char*)*cant_campos);
	const char *DELIM = "  \n";
	const char *DELIM2 ="\"";
	int i = 1;
	char *palabra = strtok(linea, DELIM);
	campos[0] = palabra;
	while(palabra != NULL && i < cant_campos){
			if(i == 1){
				palabra = strtok(NULL, DELIM2);
				campos[i] = palabra;
				i++;
			}
			else {
				palabra = strtok(NULL, DELIM);
				campos[i] = palabra;
				i++;
			}
	}
	
	return campos;
}

/* Dada una línea, la separa por campos y crea una estructura cliente con 
 * los datos de la línea
 * Pre: hash creado y se ingresa la linea por stdin.
 * Post: se guardó en el hash una nueva estructura cliente, si no se pudo
 * devuelve false */
bool cliente_guardar(hash_t* clientes, char *linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 4) return false;
	
	char ** campos = parsear_linea (linea);

	int x = atoi(campos[2]);
	int y = atoi(campos[3]);
	punto_t* coordenadas_cliente = punto_crear(x,y);
	if(!coordenadas_cliente){
		free(campos);		
		return false;
	}

	if(!hash_guardar(clientes, campos[1], coordenadas_cliente)){
		free(campos);		
		return false;
	}

	free(campos);
	return true;
}

/*Devuelve una estructura punto con los datos del cliente 
 * Pre: hash clientes creado y se ingresa la linea por stdin.
 * Post: se devolvió una estructura punto con los datos correspondientes,
 * caso contrario se devuelve NULL
 * */
punto_t* cliente_obtener(hash_t *clientes, char *linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 2) return NULL;
	
	char **campos = parsear_linea(linea);
	
	punto_t* coordenadas = hash_obtener(clientes, campos[1]);
	if(!coordenadas){
			free(campos);
			return NULL;
	}

	free(campos);
	return coordenadas;
}

/* Borra del hash el cliente pasado por parámetro, si no pudo borrar 
 * devuelve false
 * Pre: hash clientes creado y se ingresa la linea por stdin.
 * Post: se borró del hash el cliente correspondiente
 * */
bool cliente_borrar(hash_t *clientes, char *linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 2) return false;
	
	char **campos = parsear_linea(linea);
	punto_t* coordenadas = hash_obtener(clientes, campos[1]);
	if(!coordenadas){
		free(campos);
		return false;
	}

	coordenadas = hash_borrar(clientes, campos[1]);
	free(campos);
	return true;
}

/* calcula la distancia entre los dos puntos.*/
int distancia(punto_t* punto_1,punto_t* punto_2){

	int a = (punto_2->x - punto_1->x);
	if(a < 0) a = -a;
	int b = (punto_2->y - punto_1->y);
	if(b < 0) b = -b;
	a = (a + b);

	return a ;

}


/* calcula la distancia minima de las sucursales a el punto a entregar la pizza */
sucursal_t* distancia_minima(lista_t* lista, punto_t* punto){

	lista_iter_t* iter = lista_iter_crear(lista);
	sucursal_t* sucursal_1;
	sucursal_t* sucursal_2 = lista_iter_ver_actual(iter);
	int distancia_m = 1000;


	while(!lista_iter_al_final(iter)){
		sucursal_1 = lista_iter_ver_actual(iter);
		if( (distancia(punto,sucursal_1->coordenadas)) < distancia_m){
			distancia_m = distancia(punto,sucursal_1->coordenadas);
			sucursal_2 = sucursal_1;
		}
		lista_iter_avanzar(iter);
	}	
	lista_iter_destruir(iter);
	return sucursal_2;
}

/**/
sucursal_t * buscar_sucursal(hash_t *clientes, quadtree_t *quadtree, char* linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 2 && cant_campos != 3) return NULL;
	
	
	char **campos = parsear_linea(linea);
	punto_t* coordenadas = hash_obtener(clientes, campos[1]);
	if(!coordenadas){ 
		free(campos);
		return NULL;
	}
	
	lista_t* lista = quadtree_lista_puntos(quadtree,coordenadas);
	

	sucursal_t* sucursal_cercana = distancia_minima(lista,coordenadas);

	if(!sucursal_cercana){
		free(campos);
		lista_destruir(lista,NULL);
		free(sucursal_cercana);
		return NULL;
	}

	lista_destruir(lista,NULL);
	free(campos);

	return sucursal_cercana;	

}	

/* Se crea un pedido y se lo encola en el heap pedidos. No tiene prioridad este pedido
 * Pre: Se crea el heap pedidos, el quadtree, el hash clientes y se ingresa la linea por stdin.
 * Post: Se crea el pedido y se lo encola en caso de que devuelva true sino algo fallo y devuelve false.*/
bool ingresar_pedido(heap_t* pedidos, quadtree_t *quadtree, hash_t* clientes, char* linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 3) return false;
	
	size_t prioridad = 0;
	char linea2[MAX_LINEA];
	strcpy(linea2, linea);
	char **campos = parsear_linea(linea);
	size_t cant_pizzas = atoi(campos[2]);
	char* telefono = campos[1];	

	sucursal_t *sucursal = buscar_sucursal(clientes, quadtree, linea2);
	if(!sucursal){
		free(sucursal);
		free(campos);	
		return false;
	}

	pedido_t* pedido = pedido_crear(sucursal, prioridad, cant_pizzas, telefono);
	if(heap_encolar(pedidos,pedido)){
		free(campos);
		return true;
	}
	
	free(campos);
	return false;
	
	
}

/* Se crea un pedido y se lo encola en el heap pedidos. Tiene prioridad este pedido
 * Pre: Se crea el heap pedidos, el quadtree, el hash clientes y se ingresa la linea por stdin.
 * Post: Se crea el pedido y se lo encola en caso de que devuelva true sino algo fallo y devuelve false.*/
bool ingresar_pedido_urgente(heap_t* pedidos, quadtree_t *quadtree, hash_t* clientes, char* linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 3) return false;	
	
	char linea2[MAX_LINEA];
	strcpy(linea2, linea);
	char **campos = parsear_linea(linea);
	size_t cant_pizzas = atoi(campos[2]);
	size_t prioridad = cant_pizzas;
	char* telefono = campos[1];

	sucursal_t *sucursal = buscar_sucursal(clientes, quadtree, linea2);
	if(!sucursal){
		free(sucursal);
		free(campos);	
		return false;
	}

	pedido_t* pedido = pedido_crear(sucursal, prioridad, cant_pizzas, telefono);
	if(heap_encolar(pedidos,pedido)){
		free(campos);
		return true;
	}
	
	free(campos);
	return false;
	
	
	
	
}

/* Lista todos los pedidos hechos para la sucursal pedida por linea
 * Pre: Se creo el heap de pedidos y se ingreso la linea por stdin.
 * Post: Devuelve la lista de pedidos de la sucursal pedida.*/
lista_t* obtener_pedidos(heap_t* pedidos,quadtree_t* quadt, char* linea){
	//valido que la cantidad de datos ingresados sea correcta
	int cant_campos = contar_cantidad_campos(linea);
	if (cant_campos != 3) return NULL;
	char **campos = parsear_linea(linea);
	
	if(!pertenece_sucursal( quadt,campos[1] ) ) return NULL;

	lista_t* lista = lista_crear();
	heap_t* heap_aux = heap_crear(comparacion_heap);
	pedido_t* pedido;
	int cantidad_a_enlistar = atoi(campos[2]);
	int contador = 0;

	while(!heap_esta_vacio(pedidos)){
		pedido = heap_desencolar(pedidos);
		if(contador < cantidad_a_enlistar){
			if( strcmp(pedido->sucursal->nombre,campos[1]) == 0 ){ 
				lista_insertar_ultimo(lista,pedido);
				contador++;
			}else heap_encolar(heap_aux,pedido);
		}else heap_encolar(heap_aux,pedido);
 	}

	
	while(!heap_esta_vacio(heap_aux)){
		pedido = heap_desencolar(heap_aux);
		heap_encolar(pedidos,pedido);

	}


	free(campos);
	heap_destruir(heap_aux,pedido_destruir);
	return lista;

}


/*Espera el ingreso de una linea y llama a la función correspondiente.
 * Informa por pantalla el resultado de la llamada de las funciones
 * Pre: hash clientes, quadtree y heap de pedidos creados */
void ingresar_comando(hash_t * clientes, quadtree_t* quadt,heap_t* pedidos){

	char linea[MAX_LINEA];
	fgets(linea, MAX_LINEA, stdin);	
	while( !feof(stdin) ){
	
		char linea2[MAX_LINEA];
		strcpy(linea2, linea);
		char *comando = strtok(linea, " ");
		if (strcmp(comando, "cliente_guardar") == 0){
			if(cliente_guardar(clientes, linea2)) printf("OK\n");
			else printf("ERROR\n");
		}
		else if(strcmp(comando, "cliente_obtener") == 0 ){
			punto_t* coordenada = cliente_obtener(clientes, linea2);
			if(!coordenada) printf("ERROR\n");
			else printf("%d %d\n",coordenada->x,coordenada->y);
		}
		else if(strcmp(comando , "cliente_borrar") == 0){
			if(cliente_borrar(clientes, linea2)) printf("OK\n");
			else printf("ERROR\n");
		}
		else if(strcmp(comando,"buscar_sucursal") == 0){
			sucursal_t* sucursal = buscar_sucursal(clientes,quadt,linea2);
			if(sucursal)printf("%s\n",sucursal->nombre);
			else printf("ERROR\n");
		}
		else if(strcmp(comando,"ingresar_pedido") == 0){
			if(ingresar_pedido(pedidos,quadt,clientes,linea2)) printf("OK\n");
			else printf("ERROR\n");
		}
		else if(strcmp(comando,"ingresar_pedido_urgente") == 0){
			if(ingresar_pedido_urgente(pedidos,quadt,clientes,linea2)) printf("OK\n");
			else printf("ERROR\n");
		}
		else if(strcmp(comando,"obtener_pedidos") == 0){



			lista_t* lista = obtener_pedidos(pedidos,quadt,linea2);

			if(!lista) printf("ERROR\n");
			else{	pedido_t* pedido;
				char* forma_pedido;
				lista_iter_t* iter = lista_iter_crear(lista);
	
				if(lista_iter_al_final(iter)) printf("\n");

				while(!lista_iter_al_final(iter)){
					pedido = lista_iter_ver_actual(iter);
					if(pedido->prioridad != 0) forma_pedido = "urgente";
					else forma_pedido = "normal";
					printf("\"%s\" %d %s\n",pedido->telefono, pedido->cant_pizzas,forma_pedido);
					lista_iter_avanzar(iter);
				}
		
				lista_iter_destruir(iter);
				lista_destruir(lista,pedido_destruir);
			}	
		}
		else printf("ERROR\n");
	
		fgets(linea, MAX_LINEA, stdin);	
	}	
}


int main(){


	hash_t *clientes = hash_crear(free);
	quadtree_t* quadt = quadtree_crear(-32767,32767,32767,-32767);
	heap_t *pedidos = heap_crear(comparacion_heap);
	if( cargar_sucursales("sucursales.csv",quadt) ); 
	ingresar_comando(clientes,quadt,pedidos);
	quadtree_destruir(quadt);
	hash_destruir(clientes);
	heap_destruir(pedidos,pedido_destruir);
	return 0;
}
